
#define LOG_TAG "UsbHostManager"
#include "alog.h"
#include "usbhost.h"


#include <stdio.h>
#include <asm/byteorder.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

extern "C"{

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include "ch9.h"

static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t device_cond = PTHREAD_COND_INITIALIZER;

static struct usb_device *current_device = NULL;
static uint8_t read_ep;
static uint8_t write_ep;

static int usb_device_added(const char *devname, void* client_data) {
    uint16_t vendorId, productId;
    struct usb_descriptor_header* desc;
    struct usb_descriptor_iter iter;

    struct usb_device *device = usb_device_open(devname);
    if (!device) {
        LOGE("usb_device_open failed\n");
        return 0;
    }

    //const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);

    char *manufacturer = usb_device_get_manufacturer_name(device);
    char *product = usb_device_get_product_name(device);
    //char *serial = usb_device_get_serial(device);
    vendorId = usb_device_get_vendor_id(device);
    productId = usb_device_get_product_id(device);

    LOGI("VID:%04x,PID:%04x", vendorId, productId);
    LOGI("manufacturer:%s", manufacturer);
    LOGI("product:%s", product);

    if(vendorId == CCID_VID /*&& productId == CCID_PID*/){
        pthread_mutex_lock(&device_mutex);
        LOGI("Found usb device\n");
        current_device = device;
        pthread_cond_broadcast(&device_cond);
        pthread_mutex_unlock(&device_mutex);
    }
    else{
        usb_device_close(device);
        return 0;
    }

    usb_descriptor_iter_init(device, &iter);

    while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
        if (desc->bDescriptorType == USB_DT_CONFIG) {
            struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
            char *name = usb_device_get_string(device, config->iConfiguration);
            LOGI("config name:%s", name);
            free(name);
        } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
            struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
            char *name = usb_device_get_string(device, interface->iInterface);
            LOGI("interface name:%s", name);
            free(name);
        } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
            struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
            if((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
               USB_ENDPOINT_XFER_BULK){
                continue;
            }
            if(((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)){
                LOGI("end point IN:%02x\n", endpoint->bEndpointAddress);
                read_ep = endpoint->bEndpointAddress;
            }
            else{
                LOGI("end point OUT:%02x\n", endpoint->bEndpointAddress);
                write_ep = endpoint->bEndpointAddress;
            }
        }
    }
//    usb_device_close(device);
    return 0;
}

static int usb_device_removed(const char *devname, void* client_data) {
    LOGI("removed device name:%s", devname);
    pthread_mutex_lock(&device_mutex);

    if (current_device && !strcmp(usb_device_get_name(current_device), devname)) {
        fprintf(stderr, "current device disconnected\n");
        usb_device_close(current_device);
        current_device = NULL;
    }

    pthread_mutex_unlock(&device_mutex);
    return 0;
}

struct usb_device* UsbHostManager_waitForDevice(void) {
    struct usb_device* device = NULL;

    pthread_mutex_lock(&device_mutex);
    while (!current_device)
        pthread_cond_wait(&device_cond, &device_mutex);
    device = current_device;
    pthread_mutex_unlock(&device_mutex);

    return device;
}

int UsbHostManager_Device_write(struct usb_device* device, unsigned char buf[], int len, int waittime){
    int ret = usb_device_bulk_transfer(device, write_ep, buf, len, waittime);
    if(ret <= 0){
        LOGE("Write Fail:%d", ret);
    }
    return ret;
}

int UsbHostManager_Device_read(struct usb_device* device, unsigned char buf[], int len, int waittime){
    int ret = usb_device_bulk_transfer(device, read_ep, buf, len, waittime);
    if(ret <= 0){
        LOGE("Read Fail:%d", ret);
    }
    return ret;
}

void UsbHostManager_monitorUsbHostBus(void* thiz)
{
    struct usb_host_context* context = usb_host_init();
    if (!context) {
        LOGI("usb_host_init failed");
        return;
    }
    // this will never return so it is safe to pass thiz directly
    usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}

static void* monitor_thread(void *arg) {
    UsbHostManager_monitorUsbHostBus(arg);
    return NULL;
}

void UshHostManager_Init(void* thiz){
    pthread_t th;

//    if(setuid(0)){
//        LOGE("get permission fail: getuid=%d", getuid());
//        return;
//    }
//    if(setgid(0)){
//        LOGE("get permission fail: setgid", getgid());
//        return;
//    }

    pthread_create(&th, NULL, monitor_thread, NULL);
}

}
